/*
 * DocsPlugin.java
 * 
 * Copyright (c) 2007
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.dcarew.googledocs.utils;

import java.util.Collection;
import java.util.List;

import org.dcarew.googledocs.DocsPlugin;
import org.eclipse.core.runtime.IAdapterFactory;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.BaseLabelProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.eclipse.ui.model.WorkbenchAdapter;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.ui.views.properties.PropertyDescriptor;

import com.google.gdata.data.Person;
import com.google.gdata.data.docs.DocumentListEntry;
import com.google.gdata.data.spreadsheet.SpreadsheetEntry;

/**
 * A general utility class.
 * 
 * @author Devon Carew
 */
public class DocsUtils
{
	private static final String REFRESHING = "Refreshing from server...";
	
	/**
	 * Returns the result of calling the object's equals methods, properly handling nulls.
	 * 
	 * @param object1
	 * @param object2
	 * @return Returns the result of calling the object's equals methods, properly handling nulls.
	 */
	public static boolean safeEquals(Object object1, Object object2)
	{
		if (object1 == object2)
			return true;
		
		if (object1 == null || object2 == null)
			return false;
		
		return object1.equals(object2);
	}
	
	/**
	 * Sleep the indicated number of milliseconds.
	 * 
	 * @param millis the milliseconds to sleep
	 */
	public static void sleepMillis(int millis)
	{
		try
		{
			Thread.sleep(millis);
		}
		catch (InterruptedException e)
		{
			throw new RuntimeException(e);
		}
	}
	
	public static void registerWorkbenchAdapters()
	{
		// IWorkbenchAdapter
		Platform.getAdapterManager().registerAdapters(new IAdapterFactory() {
			public Object getAdapter(Object adaptableObject, Class adapterType) {
		        if (adapterType == IWorkbenchAdapter.class)
					return createSpreadsheetEntryWorkbenchAdapter();
		        if (adapterType == IPropertySource.class)
					return createSpreadsheetPropertySource((SpreadsheetEntry)adaptableObject);
				return null;
			}
			public Class[] getAdapterList() {
				return new Class[] { IWorkbenchAdapter.class, IPropertySource.class };
			}
		}, SpreadsheetEntry.class);
		
		Platform.getAdapterManager().registerAdapters(new IAdapterFactory() {
			public Object getAdapter(Object adaptableObject, Class adapterType) {
		        if (adapterType == IWorkbenchAdapter.class)
					return createDocumentEntryWorkbenchAdapter();
		        if (adapterType == IPropertySource.class)
					return createDocumentPropertySource((DocumentListEntry)adaptableObject);
				return null;
			}
			public Class[] getAdapterList() {
				return new Class[] { IWorkbenchAdapter.class, IPropertySource.class };
			}
		}, DocumentListEntry.class);
	}
	
	private static IWorkbenchAdapter createSpreadsheetEntryWorkbenchAdapter()
	{
		return new WorkbenchAdapter() {
			public ImageDescriptor getImageDescriptor(Object object) {
				return DocsPlugin.getImageDescriptor("icons/properties.gif");
			}
			public String getLabel(Object object) {
				SpreadsheetEntry spreadsheet = (SpreadsheetEntry)object;
				
				return spreadsheet.getTitle().getPlainText();
			}
		};
	}
	
	private static IWorkbenchAdapter createDocumentEntryWorkbenchAdapter()
	{
		return new WorkbenchAdapter() {
			public ImageDescriptor getImageDescriptor(Object object) {
				return DocsPlugin.getImageDescriptor("icons/file_obj.gif");
			}
			public String getLabel(Object object) {
				DocumentListEntry document = (DocumentListEntry)object;
				
				return document.getTitle().getPlainText();
			}
		};
	}
	
	private static IPropertySource createSpreadsheetPropertySource(final SpreadsheetEntry spreadsheet)
	{
		return new IPropertySource() {
			public IPropertyDescriptor[] getPropertyDescriptors()
			{
				return new IPropertyDescriptor[] {
					new PropertyDescriptor("author", "Author"),
					new PropertyDescriptor("document", "Document"),
					new PropertyDescriptor("lastModified", "Last modified")
				};
			}
			
			public Object getPropertyValue(Object id)
			{
				if (id.equals("document")) {
					return spreadsheet.getTitle().getPlainText();
				}
				if (id.equals("author")) {
					List authors = spreadsheet.getAuthors();
					
					if (authors.size() == 0)
						return null;
					else
						return ((Person)authors.get(0)).getName();
				}
				if (id.equals("lastModified")) {
					return spreadsheet.getUpdated().toUiString();
				}
				
				return null;
			}
			
			public boolean isPropertySet(Object id) {
				return false;
			}
			public void resetPropertyValue(Object id) {
			}
			public void setPropertyValue(Object id, Object value) {
			}
			public Object getEditableValue() {
				return null;
			}
		};
	}
	
	private static IPropertySource createDocumentPropertySource(final DocumentListEntry document)
	{
		return new IPropertySource() {
			public IPropertyDescriptor[] getPropertyDescriptors()
			{
				return new IPropertyDescriptor[] {
					new PropertyDescriptor("author", "Author"),
					new PropertyDescriptor("document", "Document"),
					new PropertyDescriptor("lastViewed", "Last viewed"),
					new PropertyDescriptor("lastModified", "Last modified"),
					new PropertyDescriptor("lastModifiedBy", "Last modified by")
				};
			}
			
			public Object getPropertyValue(Object id)
			{
				if (id.equals("document")) {
					return document.getTitle().getPlainText();
				}
				if (id.equals("author")) {
					List authors = document.getAuthors();
					
					if (authors.size() == 0)
						return null;
					else
						return ((Person)authors.get(0)).getName();
				}
				if (id.equals("lastViewed")) {
					return document.getLastViewed().toUiString();
				}
				if (id.equals("lastModified")) {
					return document.getUpdated().toUiString();
				}
				if (id.equals("lastModifiedBy")) {
					return document.getLastModifiedBy().getName();
				}
				
				return null;
			}
			
			public boolean isPropertySet(Object id) {
				return false;
			}
			public void resetPropertyValue(Object id) {
			}
			public void setPropertyValue(Object id, Object value) {
			}
			public Object getEditableValue() {
				return null;
			}
		};
	}
	
	public static ILabelProvider createGoogleDocsLabelProvider()
	{
		return new GoogleDocTableLabelProvider();
	}
	
	public static ITableLabelProvider createGoogleDocsTableLabelProvider()
	{
		return new GoogleDocTableLabelProvider();
	}
	
	public static IStructuredContentProvider createSpreadsheetEntryStructuredContentProvider()
	{
		return new IStructuredContentProvider() {
			public Object[] getElements(Object inputElement) {
				return ((Collection)inputElement).toArray();
			}
			public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
				
			}
			public void dispose() {
				
			}
		};
	}
	
	public static IStructuredContentProvider createSpreadsheetContentProvider()
	{
		return new SpreadsheetContentProvider();	
	}
	
	public static IStructuredContentProvider createDocumentsContentProvider()
	{
		return new DocumentsContentProvider();
	}
	
	public static String describeSpreadsheet(SpreadsheetEntry entry)
	{
		return entry.getTitle().getPlainText()
	        + " (" + ((Person)entry.getAuthors().get(0)).getEmail()
	        + ", updated " + entry.getUpdated().toUiString()
	        + ")";
	}
	
	public static String describeDocument(DocumentListEntry document)
	{
		return document.getTitle().getPlainText()
	        + " (" + ((Person)document.getAuthors().get(0)).getEmail()
	        + ", updated " + document.getUpdated().toUiString()
	        + ")";
	}
	
	static class GoogleDocTableLabelProvider
		extends BaseLabelProvider
		implements ITableLabelProvider, ILabelProvider
	{
		
	    /**
		 * Returns the label image for the given column of the given element.
		 * 
		 * @param element
		 *            the object representing the entire row, or <code>null</code> indicating that
		 *            no input object is set in the viewer
		 * @param columnIndex
		 *            the zero-based index of the column in which the label appears
		 * @return Image or <code>null</code> if there is no image for the given object at
		 *         columnIndex
		 */
		public Image getColumnImage(Object element, int columnIndex)
		{
			if (columnIndex == 0)
			{
				if (element instanceof SpreadsheetEntry)
					return DocsPlugin.getImage("icons/properties.gif");
				else if (element instanceof DocumentListEntry)
					return DocsPlugin.getImage("icons/file_obj.gif");
			}
			
			return null;
		}
		
	    /**
		 * Returns the label text for the given column of the given element.
		 * 
		 * @param element
		 *            the object representing the entire row, or <code>null</code> indicating that
		 *            no input object is set in the viewer
		 * @param columnIndex
		 *            the zero-based index of the column in which the label appears
		 * @return String or or <code>null</code> if there is no text for the given object at
		 *         columnIndex
		 */
		public String getColumnText(Object element, int columnIndex)
		{
			if (element instanceof SpreadsheetEntry)
			{
				SpreadsheetEntry spreadsheet = (SpreadsheetEntry)element;
				
				if (columnIndex == 0)
					return spreadsheet.getTitle().getPlainText();
				else if (columnIndex == 1)
					return ((Person)spreadsheet.getAuthors().get(0)).getEmail();
				else if (columnIndex == 2)
					return spreadsheet.getUpdated().toUiString();
			}
			else if (element instanceof DocumentListEntry)
			{
				DocumentListEntry entry = (DocumentListEntry)element;
				
				if (columnIndex == 0)
					return entry.getTitle().getPlainText();
				else if (columnIndex == 1)
					return ((Person)entry.getAuthors().get(0)).getEmail();
				else if (columnIndex == 2)
					return entry.getUpdated().toUiString();
			}
			
			return element.toString();
		}
		
		public Image getImage(Object element)
		{
			return getColumnImage(element, 0);
		}
		
		public String getText(Object element)
		{
			return getColumnText(element, 0);
		}
	}
	
	static class SpreadsheetContentProvider
		implements IStructuredContentProvider
	{
		private Viewer 		viewer;
		private Object[]	spreadsheets = new Object[] { REFRESHING };
		
		
		public SpreadsheetContentProvider()
		{
			
		}
		
		public Object[] getElements(Object inputElement)
		{
			return spreadsheets;
		}
		
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
		{
			this.viewer = viewer;
			
			// Start a documents search.
			new Thread(new Runnable() {
				public void run() {
					try
					{
						List docs = DocsPlugin.getAllGoogleSpreadSheets();
						
						spreadsheets = docs.toArray();
					}
					catch (Exception exception)
					{
						ErrorDialog.openError(Display.getDefault().getActiveShell(),
							"Error Getting Spreadsheet List",
							"There was a problem while trying to retreive the list of Google spreadsheets: "
								+ exception.toString(),
							new Status(IStatus.ERROR, DocsPlugin.PLUGIN_ID, exception.getMessage(), exception));
						
						spreadsheets = new Object[0];
					}
					
					updateViewer();
				}
			}).start();
		}
		
		protected void updateViewer()
		{
			Display.getDefault().asyncExec(new Runnable() {
				public void run() {
					if (viewer != null && viewer.getControl() != null && !viewer.getControl().isDisposed())
						viewer.refresh();
				}
			});
		}

		public void dispose()
		{
			
		}
	}
	
	static class DocumentsContentProvider
		implements IStructuredContentProvider
	{
		private Viewer 		viewer;
		private Object[]	spreadsheets = new Object[] { REFRESHING };
		
		
		public DocumentsContentProvider()
		{
			
		}
		
		public Object[] getElements(Object inputElement)
		{
			return spreadsheets;
		}
		
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
		{
			this.viewer = viewer;
			
			// Start a documents search.
			new Thread(new Runnable() {
				public void run() {
					try
					{
						List docs = DocsPlugin.getAllGoogleDocuments();
						
						spreadsheets = docs.toArray();
					}
					catch (Exception exception)
					{
						ErrorDialog.openError(Display.getDefault().getActiveShell(),
							"Error Getting Document List",
							"There was a problem while trying to retreive the list of Google documents: "
								+ exception.toString(),
							new Status(IStatus.ERROR, DocsPlugin.PLUGIN_ID, exception.getMessage(), exception));
						
						spreadsheets = new Object[0];
					}
					
					updateViewer();
				}
			}).start();
		}
		
		protected void updateViewer()
		{
			Display.getDefault().asyncExec(new Runnable() {
				public void run() {
					if (viewer != null && viewer.getControl() != null && !viewer.getControl().isDisposed())
						viewer.refresh();
				}
			});
		}
		
		public void dispose()
		{
			
		}
	}
	
}
